﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Diagnostics;
using Dsl.Ped.Controls.ProjectiveControl;
using Dsl.Ped.Framework.AddIns;

namespace Dsl.Ped.Editor.Services {

    /// <summary>
    /// Registers all AddIns to the editor.
    /// </summary>
    public class AddInRegistrationService : IAddInRegistrationService {

        #region Fields

        /// <summary>
        /// Dictionary of registered <see cref="IDslPedAddIn"/> instances.
        /// The key is an addIn xmlNamespace
        /// </summary>
        private Dictionary<string, IDslPedAddInMetadata> registeredAddIns;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="AddInRegistrationService"/> class.
        /// </summary>
        /// <param name="addInsToRegister">Available <see cref="IDslPedAddIn"/> instances with their <see cref="IDslPedAddInMetadata"/></param>
        public AddInRegistrationService(IEnumerable<Lazy<IDslPedAddIn, IDslPedAddInMetadata>> addInsToRegister)
        {
            // Register All available add-ins
            this.RegisterAddIns(addInsToRegister);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Get registered type by the xmlNamespace
        /// </summary>
        /// <param name="xmlNamespace">XmlNamespace of the registered addIn</param>
        /// <returns>Registered projective object's type</returns>
        public Type GetRegisteredType(string xmlNamespace) {

            return this.GetRegisteredTypeByNamespace(xmlNamespace);
        }

        /// <summary>
        /// Get a list of <see cref="ViewMode"/> available in the current xmlNamespace
        /// </summary>
        /// <param name="xmlNamespace">XmlNamespace of the registered addIn</param>
        /// <returns></returns>
        public List<ViewMode> GetRegisteredViewModes(string xmlNamespace)
        {
            return this.GetRegisteredViewModesByNamespace(xmlNamespace);
        }

        /// <summary>
        /// Get a dictionary of (xmlNamespace, projective type)
        /// </summary>
        /// <returns></returns>
        public Dictionary<string, Type> GetRegisteredTypes()
        {
            return this.registeredAddIns.ToDictionary(addIn => addIn.Key, addIn => addIn.Value.ProjectiveType);
        }

        /// <summary>
        /// Get the extension of registered projective object's type
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        /// TODO - what if there is more then one types registered for the given xmlNamespace
        public string GetNamespaceByType(Type type)
        {
            return GetRegisteredNamespaceByType(type);
        }

        // TODO - what if there is more then one types registered for the given xmlNamespace
        public ResourceDictionary GetResourceDictionaryByType(Type type)
        {
            return this.getResourceDictionary(type);
        }

        /// <summary>
        /// Register all available xmlNamespaces of the given addIns and their appropriate types
        /// </summary>
        private void RegisterAddIns(IEnumerable<Lazy<IDslPedAddIn, IDslPedAddInMetadata>> editorAddIns)
        {
            this.registeredAddIns = new Dictionary<string, IDslPedAddInMetadata>();

            if (editorAddIns == null)
            {
                return;
            }

            // For each available addIn register its type
            foreach (var addIn in editorAddIns)
            {
                Console.WriteLine(addIn.Metadata.Namespace);
                this.registeredAddIns.Add(addIn.Metadata.Namespace, addIn.Metadata);
            }
        }

        /// <summary>
        /// Get the type of the addIn registered for the given xmlNamespace. 
        /// If not registered, return null
        /// </summary>
        /// <param name="xmlNamespace">XmlNamespace of the required AddIn</param>
        /// <returns>Type of the addIn registered for the given xmlNamespace, null if not registerd</returns>
        private Type GetRegisteredTypeByNamespace(string xmlNamespace) {
            try 
            {
                var addInsMetadata = this.registeredAddIns[xmlNamespace];
                return addInsMetadata.ProjectiveType;
            } 
            catch (Exception exc) 
            {
                Debug.Write("Exception: Unregistered type: " + exc.Message);
                return null;
            }
        }

        /// <summary>
        /// Get the <see cref="ViewMode"/> of the addIn registered for the given xmlNamespace.
        /// If not registered, return null
        /// </summary>
        /// <param name="xmlNamespace">XmlNamespace of the required AddIn</param>
        /// <returns>List of <see cref="ViewMode"/> of the addIn registered for the given xmlNamespace</returns>
        private List<ViewMode> GetRegisteredViewModesByNamespace(string xmlNamespace)
        {
            try
            {
                var addInsMetadata = this.registeredAddIns[xmlNamespace];
                return addInsMetadata.ViewModes.ToList<ViewMode>();
            } catch (Exception exc)
            {
                Debug.Write("Exception: Unregistered type: " + exc.Message);
                return null;
            }
        }

        /// <summary>
        /// Get the the namespace of registered type
        /// </summary>
        /// <param name="type">Registered type</param>
        /// <returns>File-extension of the registered type</returns>
        // TODO - what if there is more then one xmlNamespace registered for the given Type
        private string GetRegisteredNamespaceByType(Type type)
        {
            foreach (var addIn in this.registeredAddIns.Where(addIn => addIn.Value.ProjectiveType == type))
            {
                return addIn.Value.Namespace;
            }

            return string.Empty;
        }

        /// <summary>
        /// Gets the resource dictionary for the given type
        /// </summary>
        /// <param name="type">Required type</param>
        /// <returns>Resource dictionary for the given type</returns>
        /// TODO - what if there is mor ethen one xmlNamespace registered for the given Type
        private ResourceDictionary getResourceDictionary(Type type)
        {
            foreach (var addIn in this.registeredAddIns.Where(addIn => addIn.Value.ProjectiveType == type))
            {
                var resourceDictionaryType = addIn.Value.ResourceDictionary;
                if (typeof(ResourceDictionary).IsAssignableFrom(resourceDictionaryType))
                {
                    return (ResourceDictionary)Activator.CreateInstance(resourceDictionaryType);
                }
                else
                {
                    throw new Exception("ResourceDictionary type is not of a type ResrouceDictionary");
                }
            }

            return null;
        }

        #endregion
    }
}
